package cz.vse.adventura.po1715.xburj54_bursa.hra;

import cz.pecinovsky.english.pojj.adv_framework.game_gui.IListener;
import cz.pecinovsky.english.pojj.adv_framework.game_gui.IPlaceG;
//import cz.pecinovsky.english.pojj.adv_framework.game_txt.IPlace;
import java.awt.Point;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static cz.vse.adventura.po1715.xburj54_bursa.hra.Předmět.NEZVEDNUTELNÝ;


//== SOUKROMÉ A POMOCNÉ METODY TŘÍDY ===========================================
/*******************************************************************************
 * Instance třídy {@code Místnost} představují jednotlive mistnosti
 * v prochazenem byte. Trida {@code Místnost} je soucasne schopna vybudovat
 * na základě zadných parametrů cely byt z jednotlivych mistnosti.
 *
 * @author Jan Bursa
 * @version 1.02,  16.12.2010
 */
public enum Místnost implements IPlaceG
{
//== HODNOTY VÝČTOVÉHO TYPU ====================================================

    /** Výchozí prostor. */
     Zahrada ( new Point(540,390),
            "Výchozí prostor",
            new String[] { "Garáž", "Vchod" },
            NEZVEDNUTELNÝ+"Květiny", NEZVEDNUTELNÝ+"Plot"
            ),
     /** Místnost se spoustou nářadí. */
     Garáž ( new Point(550,160),
            "Místnost se spoustou nářadí",
            new String[] { "Zahrada" },
            "Noviny", "Kleště", "Šroubovák", "Vrtačka"
          ),
     /** Místnost která vede do domu. */
     Vchod ( new Point(380,400),
            "Místnost která vede do domu",
            new String[] { "Zahrada" }, //,"Předsíň"},
            NEZVEDNUTELNÝ+"Rohožka", NEZVEDNUTELNÝ+"Dveře"
          ),
     /** Místo odkud se dá dostat téměř všude po domě. */
     Předsíň ( new Point(335,315),
            "Místo odkud se dá dostat téměř všude po domě",
            new String[] { "Koupelna", "Kuchyň", "Vchod"},
            NEZVEDNUTELNÝ+"Botník", NEZVEDNUTELNÝ+"Bunda"
            ),
     /** Koupelna. */
     Koupelna ( new Point(350,195),
            "Koupelna",
            new String[] { "Předsíň" },
            NEZVEDNUTELNÝ+"Umyvadlo", NEZVEDNUTELNÝ+"Vana", NEZVEDNUTELNÝ+"Zrcadlo"
            ),
     /** Kuchyň. */
     Kuchyň ( new Point(115,250),
            "Kuchyň",
            new String[] { "Předsíň", "Obývák" },
            NEZVEDNUTELNÝ+"Nádobí", NEZVEDNUTELNÝ+"Lednička", "Lžička"
            ),
     /** Obývák. */
     Obývák ( new Point(115,90),
            "Obývák",
            new String[] { "Kuchyň", "Pracovna" },
            NEZVEDNUTELNÝ+"Krb", NEZVEDNUTELNÝ+"Televize", NEZVEDNUTELNÝ+"Stolek", "Propiska", "Sešit"
            ),
     /** Místnost s důležitýmy předměty. */
     Pracovna ( new Point(350,70),
            "Pracovna",
            new String[] { "Obývák" },
            NEZVEDNUTELNÝ+"Stůl", NEZVEDNUTELNÝ+"PC", NEZVEDNUTELNÝ+"Obraz"
            );

//== KONSTANTNÍ ATRIBUTY TŘÍDY =================================================

    /** Mapa sloužící k převodu názvu místnosti na odpovídající místnost. */
     private static final Map<String,Místnost> název2místnost =
                                              new HashMap<String,Místnost>();

     /** Posluchači očekávající oznámení změně předmětů v místnosti. */
    private static final List<IListener<IPlaceG>> posluchačiPředmětů =
                                            new ArrayList<IListener<IPlaceG>>();

    /** Posluchači očekávající oznámení změně sousedů místnosti. */
    private static final List<IListener<IPlaceG>> posluchačiSousedů =
                                            new ArrayList<IListener<IPlaceG>>();



//== PROMĚNNÉ ATRIBUTY TŘÍDY ===================================================

    /** Aktuální místnost. */
    private static Místnost aktuálníMístnost;

//== STATICKÝ INICIALIZAČNÍ BLOK - STATICKÝ KONSTRUKTOR ========================

    //Teprve nyní existují všechny instance i všechny potřebné kontejnery,
    //a proto lze teprve nyní inicializovat sousedy místnosti
    static {
        //Zapamatuji si jména všech místností malými písmeny
        for (Místnost m : values()) {
            název2místnost.put(m.name().toLowerCase(), m);
        }
        //Jména výchozích sousedů převedu na sousedy a ty uložím
        for (Místnost m : values()) {
            for (int i=0;   i < m.názvyVýchozíchSousedů.length;   i++) {
                m.výchozíSousedé[i] = název2místnost.get(
                                      m.názvyVýchozíchSousedů[i].toLowerCase());
            }
            m.názvyVýchozíchSousedů = null;  //Už nebudou potřeba
        }
        inicializuj();
    }
//== KONSTANTNÍ ATRIBUTY INSTANCÍ ==============================================

    /** Popis významu a účelu dané místnosti. */
    private final String popis;

    /** Sousedé místnosti na začátku hry. */
    private final Místnost[]výchozíSousedé;

    /* Předměty v místnosti na počátku hry. */
    private final Předmět[] výchozíPředměty;

    /** Aktuální sousedé dané místnosti. */
    private final Collection<Místnost> sousedé = new ArrayList<Místnost>();

    /** Předměty nacházející se aktuálně v místnosti. */
    private final Collection<Předmět> předměty = new ArrayList<Předmět>();

    /** Pozice místnosti na plánku hry. */
    private final Point pozice;

//== PROMĚNNÉ ATRIBUTY INSTANCÍ ================================================

    /** Pomocná proměnná používaná v konstruktoru třídy
     *  pro inicializaci pole výchozíSousedé. */
    private String[] názvyVýchozíchSousedů;

//== PŘÍSTUPOVÉ METODY VLASTNOSTÍ TŘÍDY ========================================

    /***************************************************************************
     * Vrátí odkaz na aktuálni místnost,
     * tj. na místnost, v níž se právě nachází hráč.
     *
     * @return Požadovaný odkaz
     */
    static Místnost getAktuálníMístnost()
    {
        return aktuálníMístnost;
    }


    /***************************************************************************
     * Nastaví zadanou místnost jako aktuální.
     *
     * @param místnost Nastavovaná aktuální místnost
     */
    static void setAktuálníMístnost(Místnost místnost) {
        aktuálníMístnost = místnost;
        obvolejPosluchače();
    }


    /***************************************************************************
     * Vrátí odkaz na místnost se zadaným názvem.
     *
     * @param název Název hledané místnosti
     * @return Požadovaný odkaz
     */
    static Místnost getMístnost(String název)
    {
        Místnost cil = název2místnost.get(název.toLowerCase());
        return cil;
    }


    /***************************************************************************
     * Vrátí kolekci odkazů na jednotlivé místnosti.
     *
     * @return Požadovaná kolekce
     */
    static Collection<Místnost> getMístnosti()
    {
        //return new ArrayList<Místnost>(Arrays.asList(values()));
       return Collections.unmodifiableCollection(název2místnost.values());
    }


    /***************************************************************************
     * Vrátí název místnosti.
     *
     * @return Název místnosti
     */
    @Override
    public String getName()
     {
       return name();
     }


    /***************************************************************************
     * Vrátí popis místnosti, který danou místnost stručné charakterizuje.
     *
     * @return Požadovaný popis
     */
    @Override
    public String getDescription()
     {
        return popis;
     }


    /***************************************************************************
     * Vrátí kolekci předmětů nacházejících se v dané místnosti.
     *
     * @return Požadovaná kolekce
     */
    @Override
    public Collection<Předmět> getObjects()
     {
         return Collections.unmodifiableCollection(předměty);
     }


    /***************************************************************************
     * Vrátí kolekci místností, do nichž je možno se z teto místnosti přesunout.
     *
     * @return Požadovaná kolekce
     */
    @Override
    public Collection<Místnost> getNeighbors()
     {
        //return new ArrayList<Místnost>(sousedé);
        return Collections.unmodifiableCollection(sousedé);
     }

//== OSTATNÍ NESOUKROMÉ METODY TŘÍDY ===========================================

    /***************************************************************************
     * Inicializuje místnosti do výchozího stavu pro spuštění hry,
     * tj. nastavuje jim výchozí sousedy a výchozí přítomné předměty.
     * Metoda přitom předpokládá, že jsou již přihlášeni všichni posluchači.
     */
    static void inicializuj()
    {
        for (Místnost m : values()) {
            m.sousedé.clear();
            m.sousedé.addAll(Arrays.asList(m.výchozíSousedé));
            m.předměty.clear();
            m.předměty.addAll(Arrays.asList(m.výchozíPředměty));
        }
//            for (Místnost soused : m.výchozíSousedé) {
//                m.sousedé.add(soused);
//            }
//            m.předměty.clear();
//            for (Předmět predmet : m.výchozíPředměty) {
//                m.předměty.add(predmet);
//            }
//        }
        aktuálníMístnost = Zahrada;
        obvolejPosluchače();
    }

    /***************************************************************************
     * Obvolá všechny přihlášené posluchače zadaného stavu,
     * tj. posluchače předmětů i posluchače sousedů,
     * a tím jim oznámí, že se množina sledovaných objektů změnila.
     */
    static void obvolejPosluchače()
    {
        obvolejPosluchače(posluchačiPředmětů);
        obvolejPosluchače(posluchačiSousedů);
    }

    /***************************************************************************
     * Přidá zadaného posluchače do seznamu posluchačů,
     * které zpravuje o změně množiny předmětů v místnosti
     *
     * @param posluchač Přihlašovaný posluchač
     */
    static void přihlašPosluchačePředmětů(IListener<IPlaceG> posluchač)
    {
        posluchačiPředmětů.add(posluchač);
    }


    /***************************************************************************
     * Odebere zadaného posluchače ze seznamu posluchačů,
     * které zpravuje o změně množiny předmětů v místnosti
     *
     * @param posluchač Odhlašovaný posluchač
     */
    static void odhlašPosluchačePředmětů(IListener<IPlaceG> posluchač)
    {
        posluchačiPředmětů.remove(posluchač);
    }


    /***************************************************************************
     * Přidá zadaného posluchače do seznamu posluchačů,
     * které zpravuje o změně množiny sousedů místnosti
     *
     * @param posluchač Přihlašovaný posluchač
     */
    static void přihlašPosluchačeSousedů(IListener<IPlaceG> posluchač)
    {
        posluchačiSousedů.add(posluchač);
    }


    /***************************************************************************
     * Odebere zadaného posluchače ze seznamu posluchačů,
     * které zpravuje o změně množiny sousedů místnosti
     *
     * @param posluchač Odhlašovaný posluchač
     */
    static void odhlašPosluchačeSousedů(IListener<IPlaceG> posluchač)
    {
        posluchačiSousedů.remove(posluchač);
    }

//        /***************************************************************************
//     * Upozorní posluchače na změnu množiny předmětů
//     */
//    void noticeObjectListeners() {
//        for (IListener<IPlaceG> listener : posluchačiPředmětů) {
//            listener.notice(aktuálníMístnost);
//        }
//    }
//    /***************************************************************************
//    * Upozorní posluchače na změnu množiny sousedů
//    */
//    void noticeNeighborsListeners() {
//        for (IListener<IPlaceG> listener : posluchačiSousedů) {
//            listener.notice(aktuálníMístnost);
//        }
//    }
//##############################################################################
//== KONSTRUKTORY A TOVÁRNÍ METODY =============================================
/***************************************************************************
     * Vytvoří mistnost se zadanými charakteristikami, popisem účelu místnosti,
     * pozicí případného návštěvníka (= pozicí na plánku),
     * seznamem možných sousedů (kam všude se dá z místnosti jít) a
     * seznamem v místnosti se vyskytujících předmětů.
     *
     * @param popis    Popis účelu dané místnosti
     * @param sousedé  Pole s názvy sousedů dané místnosti, tj. s názvy
     *                 místností, do nichž se dá z dané místnosti přejít.
     * @param předměty Názvy předmětů, které se v místnosti vyskytují.
     *                 Konstruktor nechá tyto předměty vyrobit.
     */
    private Místnost(Point pozice, String popis, String[] sousedé, String... předměty)
    {
        this.pozice = pozice;
        this.popis  = popis;
        this.názvyVýchozíchSousedů = sousedé;
        this.výchozíSousedé  = new Místnost[sousedé.length];
        this.výchozíPředměty = new Předmět [předměty.length];
        for (int i=0;   i < předměty.length;   i++ ) {
            Předmět předmět = new Předmět(předměty[i]);
            výchozíPředměty[i] = předmět;
        }
    }


//== ABSTRAKTNÍ METODY =========================================================
//== PŘÍSTUPOVÉ METODY VLASTNOSTÍ INSTANCÍ =====================================
//== OSTATNÍ NESOUKROMÉ METODY INSTANCÍ ========================================

    /***************************************************************************
     * Odlozi zadany predmet v mistnosti.
     *
     * @param  predmet odkladany v mistnosti
     * @return {@code true} pokud byl predmet do mistnosti vlozen
     */
    void poloz( Předmět predmet ) {
        předměty.add( predmet );
        obvolejPosluchače(posluchačiPředmětů);
    }

    /***************************************************************************
     * Odebere zadany predmet z mistnosti.
     *
     * @param  predmet odebirany z mistnosti
     * @return {@code true} pokud byl predmet z mistnosti odebran
     */
    void odeber( Předmět predmet ) {
        předměty.remove( predmet );
    }

    /***************************************************************************
     * Nastaví místnosti nového souseda.
     *
     * @param  přidávaná místnost
     * @return {@code true} pokud byl soused k mistnosti vlozen
     */
    void pridejsoused( Místnost mistnost ) {
        sousedé.add( mistnost );
    }
    
    /***************************************************************************
     * Odebere predmet zadaneho nazvu z mistnosti.
     * Odebrat je mozne pouze zvednutelné předměty.
     *
     * @param název Nazev odebiraneho predmetu
     * @return Byl-li předmět z místnosti odebrán, vrátí tento předmět.
     *         Nebyl-li předmět zvednutelný, vrátí předmět {@link PEVNÝ}.
     *         Nebyl-li v místnosti požadovaný předmět, vrátí {@code null}.
     */
    Předmět zvedni(String nazev)
    {
        String jmeno;
        Předmět nasel = null;
        for( Předmět predmet : předměty ) {
            jmeno = predmet.getName();
            if (jmeno.equalsIgnoreCase( nazev ))  {
                if (predmet.isTransportable())  {
                    předměty.remove(predmet);
                    obvolejPosluchače(posluchačiPředmětů);
                    nasel = predmet;
                } else {
                    nasel = Předmět.PEVNÝ;
                }
                break;
            }
        }
        return nasel;
    }

    /***************************************************************************
     * Vrátí pozici místnosti na plánu hry,
     * presněji pozici hráče nacházejícícho se v dané místnosti.
     * Na této pozici bude hráč při pobytu v dané místnosti zobrazen.
     *
     * @return Požadovaná pozice
     */
    @Override
    public Point getPosition() {
        return pozice;
    }


/***************************************************************************
     * Obvolá všechny přihlášené posluchače zadaného stavu, čímž jím oznámí,
     * že se množina sledovaných objektů (možná) změnila.
     *
     * @param posluchači Seznam obvolávaných posluchačů
     */

    private static void obvolejPosluchače(Collection<IListener<IPlaceG>> posluchači)
    {
        for ( IListener<IPlaceG> posluchač : posluchači) {
            posluchač.notice(aktuálníMístnost);
        }
    }

//== SOUKROMÉ A POMOCNÉ METODY INSTANCÍ ========================================
//== INTERNÍ DATOVÉ TYPY =======================================================
//== TESTOVACÍ METODY A TŘÍDY ==================================================
//
//     /***************************************************************************
//      * Testovací metoda.
//      */
//     public static void test()
//     {
//          Místnost x = new Místnost();
//     }
//     /** @param args Parametry příkazového řádku - nepoužívané. */
//     public static void main(String[] args) { test(); }
}
